#include "common/exception.h"
#include "string/string.h"
#include "win32/window.h"
#include "dbg/log.h"

#include <memory>
#include "win32/lean_windows.h"
#include "CommCtrl.h"

#include "control.h"
#include "directsound.h"

//// UI Layout configuration 

int g_clientarea_width = 0;
int g_clientarea_height = 0;

const int c_button_height = 32;
const int c_button_width = 48;//c_button_height;
const int c_play_x = 0;
const int c_play_y = 0;
const int c_pause_x = c_button_width;
const int c_pause_y = 0;
const int c_stop_x = 2 * c_button_width;
const int c_stop_y = 0;
const int c_position_x = 3 * c_button_width;
const int c_position_y = 0;
int position_width() { return g_clientarea_width - c_position_x; }
const int c_position_height = c_button_height;
const int c_position_thumb_length = c_button_height - 4;

const int c_playlist_x = 0;
const int c_playlist_y = c_button_height;
int playlist_width() { return g_clientarea_width; }
int playlist_height() { return g_clientarea_height - c_playlist_y; }

//// UI elements

// Buttons
const int c_id_play = 0;

const int c_id_pause = 1;
const int c_id_stop = 2;

// Song position controller
const int c_id_position = 3;
HWND g_hwnd_position = 0;

// Playlist
const int c_id_playlist = 4;
HWND g_hwnd_playlist = 0;

HINSTANCE g_hinstance;
HWND g_hwnd;

void button_pressed(int button)
{
	switch (button)
	{
	case c_id_play:
		play();
		break;
	case c_id_pause:
		pause();
		break;
	case c_id_stop:
		stop();
		break;
	default:
		break;
	}
}

void update_position()
{
	SendMessage(g_hwnd_position, TBM_SETPOS, 1, LPARAM(position_width() * song_position()));
}

void create_child_windows(HWND hwnd)
{
	HWND result = CreateWindow(
		"button",
		"Play",
		WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON,
		c_play_x,
		c_play_y,
		c_button_width,
		c_button_height,
		hwnd,
		(HMENU) c_id_play,
		g_hinstance,
		0);
	ASSERT(result);

	result = CreateWindow(
		"button",
		"Pause",
		WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON,
		c_pause_x,
		c_pause_y,
		c_button_width,
		c_button_height,
		hwnd,
		(HMENU) c_id_pause,
		g_hinstance,
		0);
	ASSERT(result);

	result = CreateWindow(
		"button",
		"Stop",
		WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON,
		c_stop_x,
		c_stop_y,
		c_button_width,
		c_button_height,
		hwnd,
		(HMENU) c_id_stop,
		g_hinstance,
		0);
	ASSERT(result);

	RECT rect;
	GetClientRect(hwnd, &rect);

	g_clientarea_width = rect.right - rect.left;
	g_clientarea_height = rect.bottom - rect.top;

	result = CreateWindow(
		TRACKBAR_CLASS,
		"Song position",
		WS_CHILD | WS_VISIBLE | TBS_NOTICKS | TBS_BOTH | TBS_ENABLESELRANGE | TBS_FIXEDLENGTH,
		c_position_x,
		c_position_y,
		position_width(),
		c_button_height,
		hwnd,
		(HMENU) c_id_position,
		g_hinstance,
		0);
	ASSERT(result);
	g_hwnd_position = result;
	SendMessage(
		g_hwnd_position,
		TBM_SETTHUMBLENGTH,
		c_position_thumb_length,
		0);
	SendMessage(g_hwnd_position, TBM_SETRANGE, 0, MAKELONG(0, position_width()));
	update_position();

	result = CreateWindow(
		WC_LISTVIEW,
		"Playlist",
		WS_CHILD | WS_VISIBLE | LVS_LIST,
		c_playlist_x,
		c_playlist_y,
		playlist_width(),
		playlist_height(),
		hwnd,
		(HMENU) c_id_playlist,
		g_hinstance,
		0);
	ASSERT(result);
	g_hwnd_playlist = result;
}

void sizing(const RECT* rect)
{
	RECT temp = { 0, 0, 0, 0 };
	AdjustWindowRect(&temp, WS_OVERLAPPEDWINDOW, false);

	int width_offset = temp.right - temp.left;
	int height_offset = temp.bottom - temp.top;

	g_clientarea_width = rect->right - rect->left - width_offset;
	g_clientarea_height = rect->bottom - rect->top - height_offset;

	MoveWindow(g_hwnd_position, c_position_x, c_position_y, position_width(), c_position_height, false);
	SendMessage(g_hwnd_position, TBM_SETRANGE, 0, MAKELONG(0, position_width()));
	SendMessage(g_hwnd_position, TBM_SETPOS, 0, position_width() * song_position());

	MoveWindow(g_hwnd_playlist, c_playlist_x, c_playlist_y, playlist_width(), playlist_height(), false);
}

LRESULT CALLBACK wnd_proc(HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam)
{
	switch (message)
	{
	case WM_CREATE:
		create_child_windows(hwnd);
		return 0;
	case WM_ERASEBKGND:
		return 1;
	case WM_HSCROLL:
		{
			if (LOWORD(wparam) == SB_THUMBPOSITION)
			{
				float new_pos = float(HIWORD(wparam)) / position_width();
				log("hscroll to %d / %d (%f)", HIWORD(wparam), position_width(), new_pos);
				song_position(new_pos);
			}
			return 0;
		}

	case WM_TIMER:
		log("WM_TIMER!");
		directsound_update();
		if (song_position_changed())
		{
			update_position();
			InvalidateRect(g_hwnd, 0, 0);
		}
		return 0;

	case WM_DESTROY:
		PostQuitMessage(0);
		return 0;

	case WM_COMMAND:
		{
			int button_id = wparam;
			button_pressed(button_id);
			return 0;
		}

	case WM_SIZING:
		{
			RECT* rect = (RECT*) lparam;
			sizing(rect);
			return 0;
		}

	default:
		return DefWindowProc(hwnd, message, wparam, lparam);
	};
}

int __stdcall WinMain(
	HINSTANCE hinstance,
	HINSTANCE previous,
	char* cmd_line,
	int show_cmd)
{
	g_hinstance = hinstance;
	try
	{
		const char class_name[] = "plr window class";

		const WNDCLASSA window_class =
		{
			0, /*CS_HREDRAW | CS_VREDRAW*/	//UINT        style;
			wnd_proc,					//WNDPROC     lpfnWndProc;
			0,							//int         cbClsExtra;
			0,							//int         cbWndExtra;
			hinstance,					//HINSTANCE   hInstance;
			LoadIcon(0, IDI_APPLICATION),	//HICON       hIcon;
			LoadCursor(0, IDC_ARROW),		//HCURSOR     hCursor;
			(HBRUSH) GetStockObject(WHITE_BRUSH),	//HBRUSH      hbrBackground;
			0,							//LPCSTR      lpszMenuName;
			class_name,					//LPCSTR      lpszClassName;
		};

		ATOM result = RegisterClass(&window_class);
		ASSERT(result);

		g_hwnd = CreateWindowA(
			class_name,
			"Plr",
			WS_OVERLAPPEDWINDOW /*| ws_ex_composited*/,
			CW_USEDEFAULT,
			CW_USEDEFAULT,
			CW_USEDEFAULT,
			CW_USEDEFAULT,
			0,
			0,
			hinstance,
			0);
		ASSERT(g_hwnd);

		BOOL result2 = ShowWindow(g_hwnd, show_cmd);
		result2 = UpdateWindow(g_hwnd);
		ASSERT(result2);

		start_control_thread();
		directsound_init();

		SetTimer(g_hwnd, 0, 1, 0);

		MSG msg;

		while (GetMessage(&msg, 0, 0, 0))
		{
			TranslateMessage(&msg);
			DispatchMessage(&msg);
		}

		directsound_deinit();

		return int(msg.wParam);
	}
	catch (const Exception& e)
	{
		MessageBox(0, e.message(), "Exception", MB_OK);
		return 0;
	}
}